#include "master_constraints.h"
#include <ilcplex/cplex.h>
#include <stdlib.h>
#include <string.h>
#include "network.h"
#include <vector>
#include <map>
#include <iostream>

using namespace std;


void generate_flow_constraint(CPXENVptr env,CPXLPptr lp, network *net,int * status, cplexhead * head){
  int N = net->nodes();
  int E = net->linktablerows();
  int C = net->nonzerodemands();
 
  int NUMCOLS = head->getcols(); 
  int NUMROWS = N*C;
  int NUMNZ = E*C*2;
 
  int pos = 0;
  int pos_row = 0;

  int*  matind = new int [NUMNZ];
  double* matval = new double [NUMNZ];
  int*  matbeg = new int [NUMROWS];

  char * sense = new char [NUMROWS];
  double * rhs = new double [NUMROWS];

  int edge_to;
  int node_to;
  int node_from;


  //loop rows
  for(int c_row = 0; c_row < C; c_row++){
    for(int n_row =0; n_row < N; n_row++){
      matbeg[pos_row]= pos;
      pos_row++;
      //loop colums
        
      for(int e_column =0; e_column < E; e_column++){
	
	node_to = net->linktablerow((unsigned)e_column)[1];
	node_from = net->linktablerow((unsigned)e_column)[0];
	if(node_from == n_row){
	  // cout<<"Pos "<<pos<<"from: "<<node_from<<" to: "<<node_to<<"\n";
	     matind[pos] = e_column + E*c_row;
	     matval[pos] = 1;
	     pos++;
	}
	
	if(node_to == n_row){
	  // cout<<"Pos "<<pos<<"from: "<<n_row<<" to: "<<node_to<<"\n";
	  matind[pos] = e_column + E*c_row;
	  matval[pos] = -1;
	  pos++;
	}
	//	cout<<"Pos: "<<pos<<" " <<matval[pos-1]<<"\n";
	
      }
      
    }
  }
  int demand_from;
  int demand_to;
  int edge_from;
 
  pos = 0;
  for(int c_row = 0; c_row < C; c_row++){
    for(int n_row =0; n_row < N; n_row++){
      demand_from = (int)net->get_fromto_from_index((unsigned)c_row)[0];
      demand_to = (int)net->get_fromto_from_index((unsigned)c_row)[1];
      
      if(n_row == demand_from){
	rhs[pos] = 1;
	sense[pos] = 'E';
	pos++;
      }else if(n_row == demand_to){
	rhs[pos] = -1;
	sense[pos] = 'E';
	pos++;
      }else{
	rhs[pos] = 0;
	sense[pos] = 'E';
	pos++;
      }
      
    }
  }

  
  // cout<<"NUMC: "<<NUMCOLS<<" NUMROWS: "<<NUMROWS<<" NUMNZ: "<< NUMNZ<<"\n";
  /*
  for(int j = 0; j < NUMNZ;j++){ 
      cout<<"MatInd: "<<matind[j] <<" MatVal: "<< matval[j]<<"\n";
  }
    
 for(int i = 0; i < NUMROWS;i++){ 
   cout<<"MatBEG: "<<matbeg[i] <<" RHS: "<< rhs[i]<<" sign: "<<sense[i]<< "\n";   
  }
  */
  (*status) = CPXaddrows (env, lp,0 , NUMROWS, NUMNZ, rhs, sense, matbeg,
                       matind, matval, NULL, NULL);
  if ((*status) ) {
    fprintf (stderr, "Failed to populate problem constraint gen_short_path .\n");
  }
}

void generate_shortestpath_constraint(CPXENVptr env,CPXLPptr lp, network *net, int *status, cplexhead *header){
  int N = net->nodes();
  int E = net->linktablerows();
  int C = net->nonzerodemands();
 
  // int pos = 0;
  // int pos_row = 0;

  // int NUMROWS = E*C; 
  // int NUMNZ = E*C*2;

  // int*  matind = new int [NUMNZ];
  // double* matval = new double [NUMNZ];
  // int*  matbeg = new int [NUMROWS];

  // char * sense = new char [NUMROWS];
  // double * rhs = new double [NUMROWS];

  vector<int> matbeg, matcnt, matind;
  vector<double> matval;
  int NUMNZ = 0;
  map<string,vector<int> > start_of = header->getmap();
 

  for(int c = 0; c < C; c++){
    for(int e = 0; e < E; e++){
      matbeg.push_back(NUMNZ);
      matval.push_back(1);
      matind.push_back(start_of["x"][0] + c*E + e);
      NUMNZ++;
      matval.push_back(-1);
      matind.push_back(start_of["y"][0] + net->get_fromto_from_index(c)[1]*E + e);
      NUMNZ++;
    }
  }


  //     matind[pos] = e+c*E;
  //     matval[pos] = 1;
  //     pos++;
  //     matind[pos] = e+C*E+c*E;
  //     matval[pos] = -1;
  //     pos++;
  //     matbeg[pos_row] = (e + c*E)*2;
  //     pos_row++;  
      
  //   }
  // }
  int NUMROWS = C*E;
  vector<char> sense(NUMROWS, 'L');
  vector<double> rhs(NUMROWS, 0.0);

  // for(int i = 0; i < C*E; i++){
  //   sense[i] = 'L';
  //   rhs[i] = 0;
  // }
  

  (*status) = CPXaddrows (env, lp, 0, NUMROWS, NUMNZ, &rhs[0], &sense[0], &matbeg[0],
                       &matind[0], &matval[0], NULL, NULL);
  if ((*status) ) {
    fprintf (stderr, "Failed to populate problem constraint gen_short_path .\n");
  }
 


}



void generate_flowbalance_constraint(CPXENVptr env, CPXLPptr lp, network *net, cplexhead *header){
  vector<int> row;
  int number_nodes = net->nodes();
  int number_edges = net->linktablerows();
  int number_dems = header->getC(); // OD-matrisen utan diagonalen
  vector<int> matbeg, matcnt, matind;
  vector<double> matval;
  int NUMNZ = 0;
 
  int ind = 0;
  for(int demv = 0; demv < number_dems; demv++){
    for(int node = 0; node < number_nodes; node++){
      matbeg.push_back(ind);
      // matcnt.push_back(0);
      for(int edge = 0; edge < number_edges; edge++){
	row = net->linktablerow(edge);
	if(row[0] == node){
	  matval.push_back(-1.0);
	  matind.push_back(number_edges*demv + edge);
	  ind++;
	  NUMNZ++;
	  //    matcnt[demh*number_nodes + node]++;
	}else if(row[1] == node){
	  matval.push_back(1.0);
	  matind.push_back(number_edges*demv + edge);
	  ind++;
	  NUMNZ++;
	  //    matcnt[demh*number_nodes + node]++;
	}
      }
    }
  }

  for(unsigned i=0; i < matind.size(); i++){
    cout << "matind: " << matind[i] << "  matval: " << matval[i] << endl;
  }
  for(unsigned i=0; i < matbeg.size(); i++){
    cout << "matbeg: " << matbeg[i] << endl;
  }

  int NUMROWS = number_dems*number_nodes;
  int NUMCOLS = number_dems*number_edges;
  vector<char> sense(NUMROWS,'E');
  vector<double> rhs(NUMROWS, 0.0);
  // Antagande: number_dems = number_nodes*number_nodes - number_nodes
  for(int from = 0; from < number_nodes; from++){
    for(int to = 0; to < number_nodes; to++){
      if(net->demand(from,to) > 0.0){
	rhs[from*number_nodes + to] = 1;
	rhs[from*number_nodes + from] = -1;
      }
    }
  }
  NUMCOLS = 29;
  int status = CPXaddrows (env, lp, NUMCOLS, NUMROWS, NUMNZ, &rhs[0], &sense[0], &matbeg[0],
			   &matind[0], &matval[0], NULL, NULL);
  if (status){
    fprintf (stderr, "Failed to populate problem.\n");
  }
}


void generate_minz_goal(CPXENVptr env, CPXLPptr lp, int * status, cplexhead * h){
  int NUMCOLS = h->getcols(); 

  map < string, vector<int> > variabel = h->getmap();
cout<<"cols"<<h->getcols()<<"x idx"<<variabel["z"][0]<<" end "<<variabel["z"][1]<<"\n";
  double * obj = new double [NUMCOLS];
  double * ub = new double [NUMCOLS];
  double * lb = new double [NUMCOLS];
  char * ctype = new char [NUMCOLS];
  for(int i = variabel["x"][0]; i < variabel["x"][1] ;i++){
      obj[i] = 0;
      ub[i] = 1;
      lb[i] = 0;
      ctype[i] = 'C';
  }
  for(int i = variabel["y"][0]; i < variabel["y"][1] ;i++){
      obj[i] = 0;
      ub[i] = 1;
      lb[i] = 0;
      ctype[i] = 'I';
  }
  for(int i = variabel["z"][0]; i < variabel["z"][1] ;i++){
      obj[i] = 1;
      ub[i] = 1;
      lb[i] = 0;
      ctype[i] = 'C';
  }

  (*status) = CPXnewcols(env, lp, NUMCOLS, obj, lb, ub, ctype, NULL);
  if ((*status) ) {
    fprintf (stderr, "Failed to populate problem.\n");
  }

}


void generate_capacity_constraint_z(CPXENVptr env,CPXLPptr lp, network *net,int * status,cplexhead * h){
  int E = h->getE();
  int C = h->getC();

  map < string, vector<int> > variabel = h->getmap();
  
  int NUMROWS = E;
  int NUMCOLS = h->getcols();
  int NUMNZ = 0;
  
  char * sense = new char [NUMROWS];
  double * rhs = new double [NUMROWS];
  
  int length_matind = E+C*E;
  int length_matbeg = NUMROWS;

  int*  matind = new int [length_matind];
  double* matval = new double [length_matind];
  int*  matbeg = new int [length_matbeg];


 
  for(int e = 0; e < E; e++){
    sense[e] = 'L';
    rhs[e] = 0;
  }

  int demand_idx_c;
  int pos = 0;
  int pos_beg = 0;
 
 
  for(int e = 0; e < E; e++){
    matbeg[pos_beg] = pos;
    pos_beg++;
    for(int c = 0;c < C;c++){

      
      demand_idx_c =  net->get_demand_from_index((unsigned)c);
      
      matind[pos] = e + c*E;
      matval[pos] = demand_idx_c;
      pos++;
        
    }
    matind[pos] = variabel["z"][0];
    matval[pos] = -net->capacitytablerow(e)[1];
    pos++;
    NUMNZ = pos;
  }

  /*
  cout<<"NUMC: "<<NUMCOLS<<" NUMROWS: "<<NUMROWS<<"NUMNZ: "<< NUMNZ<<"\n";
  for(int j = 0; j < length_matind;j++){ 
      cout<<"MatInd: "<<matind[j] <<"MatVal: "<< matval[j]<<"\n";
  }
    
 for(int i = 0; i < length_matbeg;i++){ 
   cout<<"MatBEG: "<<matbeg[i] <<" RHS: "<< rhs[i]<<" sign: "<<sense[i]<< "\n";   
  }
   
  cout<<"Ok untile addrows";
  */
  (*status) = CPXaddrows (env, lp,0, NUMROWS, NUMNZ, rhs, sense, matbeg,
			  matind, matval, NULL, NULL);
  if ((*status) ) {
    fprintf (stderr, "Failed to populate problem constraint gen_short_path .\n");
  }
}


void generate_no_split_constraint_z(CPXENVptr env, CPXLPptr lp, network *net,int * status, cplexhead *h){
  int E = h->getE();
  int C = h->getC();
  int N = h->getN();
  map < string, vector<int> > start_of = h->getmap();
  vector<double> matval;
  vector<int> matbeg, matind;

  int NUMNZ = 0;

  for(int l = 0; l < N; l++){
    for(int i = 0; i < N; i++){
      if(i != l){
	matbeg.push_back(NUMNZ);
	for(int j = 0; j < N; j++){
	  if(net->get_link_nr(i,j) > -1){
	    matval.push_back(1);
	    matind.push_back(start_of["y"][0] + l*E + net->get_link_nr(i,j));
	    NUMNZ++;
	  }
	}
      }
    }
  }
  
  int NUMROWS = N*(N-1);

  vector<char> sense(NUMROWS, 'L');
  vector<double> rhs(NUMROWS, 1.0);

 //  int NUMROWS = N*C;
 //  int NUMCOLS = h->getcols();
 //  int NUMNZ = E*C;
  
 //  char * sense = new char [NUMROWS];
 //  double * rhs = new double [NUMROWS];

 //  int*  matind = new int [NUMNZ];
 //  double* matval = new double [NUMNZ];
 //  int*  matbeg = new int [NUMROWS];

 
  
 //  int from_node;
 //  int pos = 0;
 //  int pos_row = 0;
 
 //  for(int c = 0; c<C; c++){
 //    for(int n = 0; n<N; n++){
    
 //      matbeg[pos_row] = pos;
 //      pos_row++;
            
 //      for(int e = 0; e < E; e++){
 // 	if(n == net->linktablerow((unsigned)e)[0]){
 // 	  matind[pos] = variabel["y"][0] + e + c*E;
 // 	  matval[pos] = 1;
 // 	  pos++;
 // 	}
 //      }	
      
 //    }
 //  }
 // pos = 0;
 // for(int c = 0; c<C; c++){
 //    for(int n = 0; n < N; n++){
 //      sense[pos] = 'L';
 //      rhs[pos] = 1;
 //      pos++;
 //    }
 //  }

  (*status) = CPXaddrows (env, lp,0, NUMROWS, NUMNZ, &rhs[0], &sense[0], &matbeg[0],
			  &matind[0], &matval[0], NULL, NULL);
  if ((*status) ) {
    fprintf (stderr, "Failed to populate problem constraint gen_short_path .\n");
  }
}

void fix_y_from_demand(CPXENVptr env,CPXLPptr lp, network *net,int * status,cplexhead * h){
  int E = h->getE();
  int C = h->getC();
  int N = h->getN();
  map < string, vector<int> > variabel = h->getmap();
  
  int count = 0;
  int to_node;
  for(int n = 0; n<N; n++){
    for(int e = 0; e < E; e++){
      if(net->linktablerow((unsigned)e)[0] == n){
	count++;
      }
    }
  }
  
  int NUMROWS = N;
  int NUMCOLS = h->getcols();
  int NUMNZ = count;
  
  char * sense = new char [NUMROWS];
  double * rhs = new double [NUMROWS];

  int*  matind = new int [NUMNZ];
  double* matval = new double [NUMNZ];
  int*  matbeg = new int [NUMROWS];
  
  int from_node;
  int pos = 0;
  int pos_row = 0;

  for(int n = 0; n<N; n++){
    matbeg[pos_row] = pos;
    pos_row++;
    // cout<<"to :"<<to_node<<" commodety "<<c<<"\n";
    for(int e = 0; e < E; e++){
      if(net->linktablerow((unsigned)e)[0] == n){
	matind[pos] = variabel["y"][0]+e +n*E;
	matval[pos] = 1;
	pos++;
      }
    }
  }
  /* for(int i =0;i<NUMNZ;i++){
    cout<<"matind: "<<i<<" "<<matind[i]<<" "<<matval[i]<<"\n";
  }
  */
  
 pos = 0;
 for(int n = 0; n<N; n++){
     sense[pos] = 'E';
     rhs[pos] = 0;
     pos++;
   
 }
 


  (*status) = CPXaddrows (env, lp,0, NUMROWS, NUMNZ, rhs, sense, matbeg,
			  matind, matval, NULL, NULL);
  if ((*status) ) {
    fprintf (stderr, "Failed to populate problem constraint fix y from .\n");
  }
}


void generate_capacity_constraint(CPXENVptr env, CPXLPptr lp, network *net, cplexhead *header){
  int E = net->linktablerows();
  int C = header->getC();
  int beg = 0;
  vector<int> matbeg, matcnt, matind;
  vector<double> matval;
  int NUMNZ = 0;
 
  for(int e = 0; e < E; e++){
    matbeg.push_back(beg);
    for(int d = 0; d < C; d++){
      matval.push_back(net->demand(net->get_fromto_from_index(d)[0],net->get_fromto_from_index(d)[1]));
      matind.push_back(d*E + e);
      NUMNZ++;
      beg++;
    }
  }

  vector<char> sense(E,'L');
  vector<double> rhs(E, 0.0);
  for(int e = 0; e < E; e++){
    rhs[e] = net->capacitytablerow(e)[1];
  }

  int NUMROWS = E;
  int status = CPXaddrows (env, lp, 0, NUMROWS, NUMNZ, &rhs[0], &sense[0], &matbeg[0],
		       &matind[0], &matval[0], NULL, NULL);
  if (status){
    fprintf (stderr, "Failed to populate problem.\n");
  }
}



void generate_u_constraint(CPXENVptr env, CPXLPptr lp, network *net, cplexhead *header){
  int E = net->linktablerows();
  int C = header->getC();
  int beg = 0;
  vector<int> matbeg, matcnt, matind;
  vector<double> matval;
  int NUMNZ = 0;
  map<string,vector<int> > start_of = header->getmap();
 
  for(int e = 0; e < E; e++){ 
    matbeg.push_back(beg);
    for(int d = 0; d < C; d++){
      matval.push_back(net->demand(net->get_fromto_from_index(d)[0],net->get_fromto_from_index(d)[1]));
      matind.push_back(d*E + e);
      NUMNZ++;
      beg++;
    }
    matval.push_back(-net->capacitytablerow(e)[1]);
    matind.push_back(start_of["u"][0] + e);
    NUMNZ++;
    beg++;
  }

  vector<char> sense(E,'L');
  vector<double> rhs(E, 0.0);

  int NUMROWS = E;
  int status = CPXaddrows(env, lp, 0, NUMROWS, NUMNZ, &rhs[0], &sense[0], &matbeg[0],
		       &matind[0], &matval[0], NULL, NULL);
  if (status){
    fprintf (stderr, "Failed to populate problem.\n");
  }
}


void generate_u_obj(CPXENVptr env, CPXLPptr lp, network *net, int *status, cplexhead *header){

  map<string,vector<int> > start_of = header->getmap();
  int NUMCOLS = header->getcols();
  vector<double> obj(NUMCOLS, 0.0);
  vector<char> ctype(NUMCOLS, 'C');
  vector<double> ub(NUMCOLS, 0.0);
  vector<double> lb(NUMCOLS, 0.0);
 
  for(int i = start_of["x"][0]; i < start_of["x"][1] ;i++){
    obj[i] = 0;
    ub[i] = 1;
    lb[i] = 0;
    ctype[i] = 'C';
  }
  for(int i = start_of["y"][0]; i < start_of["y"][1] ;i++){
    obj[i] = 0;
    ub[i] = 1;
    lb[i] = 0;
    ctype[i] = 'C';
  }
  for(int i = start_of["z"][0]; i < start_of["z"][1] ;i++){
    obj[i] = 0;
    ub[i] = 1;
    lb[i] = 0;
    ctype[i] = 'C';
  }
  for(int i = start_of["u"][0]; i < start_of["u"][1] ;i++){
    obj[i] = net->capacitytablerow(i-start_of["u"][0])[2];
    ub[i] = 1;
    lb[i] = 0;
    ctype[i] = 'C';
  }

  *status = CPXnewcols (env, lp, NUMCOLS, &obj[0], &lb[0], &ub[0], &ctype[0], NULL);

  if ( *status ) {
    fprintf (stderr, "Failed to populate problem.\n");
  }
}
